#include <cerrno>
#include <syslog.h>
#include <unistd.h>
#include <cstring>
#include "Epoll.h"
#include "Channel.h"

Epoll::Epoll()
{
    epollFd = epoll_create(1);
    if (epollFd == -1) {
        syslog(LOG_ERR, "epoll_create() failed(%d).\n", errno);
        exit(-1);
    }
}

Epoll::~Epoll()
{
    ::close(epollFd);
}

void Epoll::UpdateChannel(Channel *ch) const
{
    epoll_event ev{};
    ev.data.ptr = ch;
    ev.events = ch->events;
    if (ch->inEpoll) {
        int ret = epoll_ctl(epollFd, EPOLL_CTL_MOD, ch->fd, &ev);
        if (ret == -1) {
            syslog(LOG_ERR, "epoll_ctl(EPOLL_CTL_MOD) failed.\n");
            return;
        }
    } else {
        int ret = epoll_ctl(epollFd, EPOLL_CTL_ADD, ch->fd, &ev);
        if (ret == -1) {
            syslog(LOG_ERR, "epoll_ctl(EPOLL_CTL_ADD) failed.\n");
            return;
        }
        ch->inEpoll = true;
    }
}

void Epoll::RemoveChannel(Channel *ch) const
{
    if (!ch->inEpoll) {
        return;
    }
    if (::epoll_ctl(epollFd, EPOLL_CTL_DEL, ch->fd, nullptr) == -1) {
        syslog(LOG_ERR, "epoll_ctl(EPOLL_CTL_DEL) failed.\n");
        return;
    }
}

std::vector<Channel *> Epoll::Wait(int timeout)
{
    std::vector<Channel *> channels;
    bzero(epollEvents, sizeof(epollEvents));
    int inFds = ::epoll_wait(epollFd, epollEvents, MaxEvents, timeout);
    if (inFds < 0) {
        syslog(LOG_ERR, "epoll_wait() failed");
        return channels;
    }
    if (inFds == 0) {
        return channels;
    }
    for (int i = 0; i < inFds; i++) {
        auto *ch = (Channel *) epollEvents[i].data.ptr;
        ch->revents = epollEvents[i].events;
        channels.push_back(ch);
    }
    return channels;
}